package com.liuqi.loggers.core.encode;

import com.liuqi.loggers.core.Converter;
import com.liuqi.loggers.core.Encoder;
import com.liuqi.loggers.core.LifeCycleBase;
import com.liuqi.loggers.core.LoggerEvent;
import com.liuqi.loggers.core.converters.ThrowableConverter;
import com.liuqi.loggers.core.util.ConverterParser;
import java.io.UnsupportedEncodingException;
import java.util.List;

/**
 * 日志输编码格式化, 支持 String, Json
 *
 * {@link EncoderFormat}
 *
 * @author liuqi
 * @date 2024/11/5 17:03
 **/
public class PatternEncoder extends LifeCycleBase implements Encoder {

    private List<Converter> converters;

    private String pattern;
    private String charset;
    private EncoderFormat format = EncoderFormat.STRING;

    @Override
    public void start() {
        if (pattern == null || pattern.isEmpty()) {
            throw new NullPointerException("pattern cannot be empty");
        }
        this.converters = new ConverterParser(pattern).parse();
        super.start();
    }

    @Override
    public byte[] encode(LoggerEvent event) {
        String msg = format(event);
        try {
            return msg.getBytes(charset);
        } catch (UnsupportedEncodingException e) {
            throw new IllegalStateException("An existing charset cannot possibly be unsupported. charset = " + charset);
        }
    }

    private String format(LoggerEvent event) {
        if (format == EncoderFormat.STRING) {
            return formatString(event);
        } else {
            return formatJson(event);
        }
    }

    private String formatString(LoggerEvent event) {
        StringBuilder buf = new StringBuilder();
        for (Converter converter : converters) {
            String convert = converter.convert(event);
            if (convert != null) buf.append(convert);

        }
        return buf.toString();
    }

    private String formatJson(LoggerEvent event) {
        StringBuilder buf = new StringBuilder();
        buf.append("{");
        for (int i = 0; i < converters.size(); i++) {
            Converter converter = converters.get(i);
            String name = converter.name();
            if (name == null) continue;
            String value = converter.convert0(event);
            if (converter instanceof ThrowableConverter) {
                if (value != null) {
                    value = value.replace("\n", "\\n")
                            .replace("\r", "\\r")
                            .replace("\t", "\\t");
                }
            }
            buf.append("\"")
                    .append(name)
                    .append("\"")
                    .append(":")
                    .append("\"")
                    .append(value)
                    .append("\"");
            if (i + 1 < converters.size()) {
                buf.append(",");
            }
        }
        buf.append("}");
        return buf.toString();
    }

    public String getPattern() {
        return pattern;
    }

    public void setPattern(String pattern) {
        this.pattern = pattern;
    }

    public String getCharset() {
        return charset;
    }

    public void setCharset(String charset) {
        this.charset = charset;
    }

    public EncoderFormat getFormat() {
        return format;
    }

    public void setFormat(EncoderFormat format) {
        this.format = format;
    }

}
